package main

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"sync"
	"time"

	"github.com/shirou/gopsutil/v3/cpu"
	"github.com/shirou/gopsutil/v3/disk"
	"github.com/shirou/gopsutil/v3/mem"

	"monitor/config"
)

// 系统配置
var sysConfig = config.GetDefaultConfig()

// 命令行参数
var (
	daemonMode = flag.Bool("d", false, "以daemon模式运行")
	noDaemon   = flag.Bool("no-daemon", false, "不执行daemon模式(内部使用)")
)

// SystemInfo 系统信息结构体
type SystemInfo struct {
	CPU       float64 `json:"cpu"`        // CPU使用率（百分比）
	Mem       uint64  `json:"mem"`        // 当前内存占用（字节）
	MemTotal  uint64  `json:"mem_total"`  // 总内存（字节）
	Disk      uint64  `json:"disk"`       // 当前硬盘占用（字节）
	DiskTotal uint64  `json:"disk_total"` // 硬盘总量（字节）
}

// 全局变量用于存储采样数据
var (
	systemInfoCache SystemInfo
	infoMutex       sync.RWMutex
	initialized     bool = false
)

// 验证签名
func verifySign(timestamp, sign string) bool {
	// 验证时间戳是否有效（可选：检查时间戳是否在合理范围内，例如5分钟内）
	ts, err := strconv.ParseInt(timestamp, 10, 64)
	if err != nil {
		log.Printf("解析时间戳失败: %v", err)
		return false
	}

	now := time.Now().Unix()
	// 时间戳有效期为5分钟
	if now-ts > 300 || ts-now > 300 {
		log.Printf("时间戳过期: %d", ts)
		return false
	}

	// 计算预期的签名：MD5(timestamp + AuthKey)
	data := timestamp + config.StaticKey + sysConfig.AuthKey
	hash := md5.Sum([]byte(data))
	expectedSign := hex.EncodeToString(hash[:])

	log.Printf("预期签名: %s, 实际签名: %s", expectedSign, sign)

	// 比较签名
	return sign == expectedSign
}

// 获取系统信息
// 获取系统信息
func getSystemInfo() (SystemInfo, error) {
	var info SystemInfo
	var err error

	// 获取CPU使用率（3秒平均值）
	cpuPercent, err := cpu.Percent(3*time.Second, false)
	if err != nil {
		return info, fmt.Errorf("获取CPU信息失败: %v", err)
	}
	if len(cpuPercent) > 0 {
		info.CPU = cpuPercent[0]
	}

	// 获取内存信息
	memInfo, err := mem.VirtualMemory()
	if err != nil {
		return info, fmt.Errorf("获取内存信息失败: %v", err)
	}
	info.Mem = memInfo.Used
	info.MemTotal = memInfo.Total

	// 获取当前程序所在磁盘信息
	execPath, err := os.Executable()
	if err != nil {
		return info, fmt.Errorf("获取程序路径失败: %v", err)
	}

	// 获取程序所在的绝对路径
	absPath, err := filepath.EvalSymlinks(execPath)
	if err != nil {
		absPath = execPath
	}

	// 获取程序所在的目录
	programDir := filepath.Dir(absPath)

	// 获取程序所在分区的使用情况
	diskInfo, err := disk.Usage(programDir)
	if err != nil {
		return info, fmt.Errorf("获取磁盘信息失败: %v", err)
	}
	info.Disk = diskInfo.Used
	info.DiskTotal = diskInfo.Total

	return info, nil
}

// 定期采样数据的协程
func samplingRoutine() {
	// 初始采样，确保有数据可用
	sampleData()
	initialized = true

	// 每3秒采样一次
	ticker := time.NewTicker(3 * time.Second)
	defer ticker.Stop()

	for range ticker.C {
		sampleData()
	}
}

// 采样数据并更新缓存
func sampleData() {
	info, err := getSystemInfo()
	if err != nil {
		log.Printf("采样数据失败: %v", err)
		return
	}

	// 更新缓存数据
	infoMutex.Lock()
	systemInfoCache = info
	infoMutex.Unlock()
}

// 处理HTTP请求
func handleRequest(w http.ResponseWriter, r *http.Request) {
	// 只接受GET请求
	if r.Method != http.MethodGet {
		http.Error(w, "只支持GET请求", http.StatusMethodNotAllowed)
		return
	}

	// 获取请求参数
	timestamp := r.URL.Query().Get("time")
	sign := r.URL.Query().Get("sign")

	// 验证签名
	if !verifySign(timestamp, sign) {
		http.Error(w, "验证失败", http.StatusUnauthorized)
		return
	}

	// 等待初始化完成
	for !initialized {
		time.Sleep(100 * time.Millisecond)
	}

	// 直接从内存中获取数据
	infoMutex.RLock()
	info := systemInfoCache
	infoMutex.RUnlock()

	// 设置响应头
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)

	// 返回JSON数据
	if err := json.NewEncoder(w).Encode(info); err != nil {
		log.Printf("编码JSON响应失败: %v", err)
	}
}

func daemonize() {
	// 准备子进程参数(保留-d参数，添加--no-daemon)
	args := append(os.Args[1:], "--no-daemon")

	// 创建子进程
	cmd := exec.Command(os.Args[0], args...)
	if err := cmd.Start(); err != nil {
		log.Fatalf("启动daemon失败: %v", err)
	}

	// 记录子进程PID
	log.Printf("Daemon进程已启动，PID: %d", cmd.Process.Pid)
	os.Exit(0)
}

func main() {
	// 解析命令行参数
	flag.Parse()

	// 如果是daemon模式且没有--no-daemon参数，则后台运行
	if *daemonMode && !*noDaemon {
		daemonize()
	}

	// 启动数据采样协程
	go samplingRoutine()

	// 注册HTTP处理函数
	http.HandleFunc("/", handleRequest)

	// 启动HTTP服务
	addr := fmt.Sprintf(":%d", sysConfig.DefaultPort)
	log.Printf("Agent服务启动，监听端口: %d，认证密钥: %s", sysConfig.DefaultPort, sysConfig.AuthKey)
	if err := http.ListenAndServe(addr, nil); err != nil {
		log.Fatalf("启动HTTP服务失败: %v", err)
	}
}
